#include "StdAfx.h"
#include "stdio.h"
#include "TGVideoDecMan.h"

#ifdef H264_OLD_DEC
#pragma comment(lib,"avcdec.lib")
#elif H264_DEC
#ifdef _DEBUG
#pragma comment(lib,"h264decode.lib")
#else
#pragma comment(lib,"ffmpglib.lib")
#endif
#elif H264_DEC_FFMPEG

#endif

#pragma comment(lib,"avcodec.lib")


#include "malloc.h"

TGVideoDecMan::TGVideoDecMan(void)
{
#ifdef dkq_decdk_test
	char sName[50];
	sprintf(sName,"TGDecSDK_%x",this);
	m_xLog.CreateLogDir(sName,0x1001);
#endif

	
#ifdef H264_OLD_DEC
	m_pAvcDec = NULL;

	m_lEncFrameIndex = 0;

	m_lDecBufSize = 0;
	m_pDecBuffer  = NULL; 
	
	memset(m_avcDecBufTab,0,sizeof(m_avcDecBufTab));
#endif

#ifdef H264_DEC_FFMPEG
	picture = NULL;
	codec   = NULL;
	c       = NULL;
#endif

#ifdef dkq_write_264
	m_h264 = NULL;
	m_h264log = NULL;
#endif

	m_nVideoWidth  = 0;
	m_nVideoHeight = 0;
	
}

TGVideoDecMan::~TGVideoDecMan(void)
{
	UnInitialize();
}

void TGVideoDecMan::UnInitialize()
{
#ifdef H264_OLD_DEC
	if( NULL != m_pAvcDec )
	{
		avcdClose(m_pAvcDec);
		m_pAvcDec = NULL;
	}

	m_lDecBufSize = 0;
	if(m_avcDecBufTab[0].y)
	{
		_aligned_free(m_avcDecBufTab[0].y);
		_aligned_free(m_avcDecBufTab[0].u);
		_aligned_free(m_avcDecBufTab[0].v);
		m_avcDecBufTab[0].y = NULL;
	}
	if(m_avcDecBufTab[1].y)
	{
		_aligned_free(m_avcDecBufTab[1].y);
		_aligned_free(m_avcDecBufTab[1].u);
		_aligned_free(m_avcDecBufTab[1].v);
		m_avcDecBufTab[1].y = NULL;
	}
#elif H264_DEC
	if( NULL != m_pAvcDec )
	{
		CloseVideoDecoder(m_pAvcDec);
		m_pAvcDec = NULL;
	}
#elif H264_DEC_FFMPEG
	if( c ) 
	{
		avcodec_close( c) ;
		c = NULL;
	}
	if( NULL != picture ) 
	{
		avcodec_free_frame(&picture);
		picture = NULL;
	}
#endif

#ifdef dkq_write_264
	if( NULL != m_h264 )
	{
		fclose(m_h264);
		m_h264 = NULL;
	}
	if( NULL != m_h264log )
	{
		fclose( m_h264log );
		m_h264log = NULL;
	}
#endif
}


#ifdef H264_OLD_DEC
int TGVideoDecMan::Decode(BYTE* pEncFrame,long lEncSize,TG_VIDEO_DEC_INFO *pOutDecInfo)
{
	if( NULL == pEncFrame || 0 >= lEncSize || NULL == pOutDecInfo)
	{
		return TG_ERR_PARAMETER_ERROR;
	}

	pOutDecInfo->lColorSpace  = TG_COLSP_YV12;
	pOutDecInfo->nVideoWidth  = 0;
	pOutDecInfo->nVideoHeight = 0;
	pOutDecInfo->lDecSize     = 0;
	pOutDecInfo->pDecBuffer   = NULL;
	
	TG_FRAME_HEAD *pFrameHead = (TG_FRAME_HEAD*)pEncFrame;
	
	if( NULL == m_pAvcDec )
	{
		m_pAvcDec = avcdOpen(0,0);

		m_lEncFrameIndex = 0;
	}

#ifdef dkq_test_video_decode
	m_xLog.WriteLog(__FILE__,__LINE__,"start decode,size:%d ########",pFrameHead->nFrameSize);
#endif

	long lNalOffset = pFrameHead->cHeadSize;

	TG_NAL_HEAD *pNalHead = NULL;
	BYTE         *pNalData = NULL;

	avcdYUVbuffer_s * recoBuf = &m_avcDecBufTab[ (m_lEncFrameIndex & 1) ];
	avcdYUVbuffer_s * refBuf  = &m_avcDecBufTab[ (~m_lEncFrameIndex & 1) ];
	
	int nRet = AVCD_OK;

	__try
	{
		while( lNalOffset < lEncSize )
		{
			if( nRet != AVCD_OK_STREAM_NOT_DECODED )
			{
				pNalHead = (TG_NAL_HEAD*)( pEncFrame + lNalOffset );
				lNalOffset += sizeof(TG_NAL_HEAD);
				pNalData = pEncFrame + lNalOffset;
				lNalOffset += pNalHead->lNalSize;
				if( pNalHead->cNalType < TG_NAL_SLICE_SPS || pNalHead->cNalType > TG_NAL_SLICE_IDR )
				{
					continue;
				}
				if( pNalHead->lNalSize <= 0 )
				{
					break;
				}
			}
#ifdef dkq_test_video_decode
			m_xLog.WriteLog(__FILE__,__LINE__,"Input NalSize:%d ******",pNalHead->lNalSize);
#endif
			nRet = avcdDecodeOneNal(m_pAvcDec,pNalData,pNalHead->lNalSize,recoBuf,refBuf);
			if( recoBuf->picType == AVCD_PIC_SPS )
			{
				m_nVideoWidth  = recoBuf->width;
				m_nVideoHeight = recoBuf->height;

				unsigned ysize = recoBuf->width * recoBuf->height;
				if(ysize != m_lDecBufSize )
				{ 
					m_lDecBufSize = ysize;
					if(m_avcDecBufTab[0].y)
					{
						_aligned_free(m_avcDecBufTab[0].y);
 						_aligned_free(m_avcDecBufTab[0].u);
 						_aligned_free(m_avcDecBufTab[0].v);
						m_avcDecBufTab[0].y = NULL;
					}
					if(m_avcDecBufTab[1].y)
					{
						_aligned_free(m_avcDecBufTab[1].y);
 						_aligned_free(m_avcDecBufTab[1].u);
 						_aligned_free(m_avcDecBufTab[1].v);
						m_avcDecBufTab[1].y = NULL;
					}
					unsigned uvsize = ysize >> 2;
// 					recoBuf->y = _aligned_malloc(ysize + uvsize * 2 , 16);
// 					recoBuf->u = (BYTE *)recoBuf->y + ysize;
// 					recoBuf->v = (BYTE *)recoBuf->u + uvsize;
// 					refBuf->y = _aligned_malloc(ysize + uvsize * 2 , 16);
// 					refBuf->u = (BYTE *)refBuf->y + ysize;
// 					refBuf->v = (BYTE *)refBuf->u + uvsize;
					recoBuf->y = _aligned_malloc(ysize  , 16);
					recoBuf->u = _aligned_malloc(uvsize , 16);
					recoBuf->v = _aligned_malloc(uvsize , 16);
					refBuf->y  = _aligned_malloc(ysize  , 16);
					refBuf->u  = _aligned_malloc(uvsize , 16);
					refBuf->v  = _aligned_malloc(uvsize , 16);
				}
			}
		
			if( (nRet == AVCD_OK)||
				(nRet == AVCD_OK_IDR)||
				(nRet == AVCD_OK_REF)||
				(nRet == AVCD_OK_NON_REF) )
			{
				if(nRet != AVCD_OK_NON_REF)
				{
					m_lEncFrameIndex++;
				}
				pOutDecInfo->pDecBuffer = (BYTE*)recoBuf->y;
				pOutDecInfo->lDecSize   = ( m_nVideoWidth * m_nVideoHeight * 3 ) >> 1;
				pOutDecInfo->nVideoWidth  = m_nVideoWidth;
				pOutDecInfo->nVideoHeight = m_nVideoHeight;
				pOutDecInfo->y = (BYTE*)recoBuf->y;
				pOutDecInfo->u = (BYTE*)recoBuf->u;
				pOutDecInfo->v = (BYTE*)recoBuf->v;
				pOutDecInfo->lDecPitch = m_nVideoWidth;

#ifdef dkq_test_video_decode
				m_xLog.WriteLog(__FILE__,__LINE__,"decode success:%d  ++++++",nRet);
#endif

				return TG_ERR_SUCCESS;
			}
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER )
	{
	}

#ifdef dkq_test_video_decode
	m_xLog.WriteLog(__FILE__,__LINE__,"decode error:%d  &&&&&&&",nRet);
#endif


	return TG_ERR_FAILURE;
}

#elif H264_DEC

int TGVideoDecMan::Decode(BYTE* pEncFrame,long lEncSize,TG_VIDEO_DEC_INFO *pOutDecInfo)
{
	if( NULL == pEncFrame || 0 >= lEncSize || NULL == pOutDecInfo)
	{
		return TG_ERR_PARAMETER_ERROR;
	}

	pOutDecInfo->lColorSpace  = TG_COLSP_YV12;
	pOutDecInfo->nVideoWidth  = 0;
	pOutDecInfo->nVideoHeight = 0;
	pOutDecInfo->lDecSize     = 0;
	pOutDecInfo->pDecBuffer   = NULL;

	
ReStartDecode:

	if( NULL == m_pAvcDec )
	{
		m_pAvcDec = (void*)OpenVideoDecoder();
	}

#ifdef dkq_write_264
	if( NULL == m_h264 )
	{
		m_h264 = fopen("test.264","wb");
	}
	if( NULL == m_h264log )
	{
		m_h264log = fopen("test_log.txt","wt");
	}
#endif


	dec_info_s dec_Info;
	memset(&dec_Info,-1,sizeof(dec_info_s));


	TG_FRAME_HEAD *pFrameHead = (TG_FRAME_HEAD*)pEncFrame;
	long lNalOffset = pFrameHead->cHeadSize;

	TG_NAL_HEAD *pNalHead = NULL;
	UINT8       *pNalData = NULL;
	UINT32       nNalLen  = 0;

#ifdef dkq_write_264
	if( NULL != m_h264log )
	{
		fprintf( m_h264log, "num:%d,size:%d\n", pFrameHead->nFrameNum,pFrameHead->nFrameSize );
	}
#endif

	int nRet = AVCD_OK;

	__try
	{
		while( lNalOffset < lEncSize )
		{
			if( 0x01 == (pFrameHead->cFrameType & 0x03) )
			{
				if( nRet != AVCD_OK_STREAM_NOT_DECODED )
				{
					pNalHead = (TG_NAL_HEAD*)( pEncFrame + lNalOffset );
					lNalOffset += sizeof(TG_NAL_HEAD);
					pNalData = pEncFrame + lNalOffset;
					lNalOffset += pNalHead->lNalSize;
					if( pNalHead->cNalType < TG_NAL_SLICE_SPS || pNalHead->cNalType > TG_NAL_SLICE_IDR )
					{
						continue;
					}
					if( pNalHead->lNalSize <= 0 )
					{
						break;
					}
					nNalLen = pNalHead->lNalSize;
				}
			}
			else
			{
				pNalData = pEncFrame + sizeof(TG_FRAME_HEAD);
				nNalLen  = pFrameHead->nFrameSize;

				lNalOffset += pFrameHead->nFrameSize;
			}

#ifdef dkq_write_264
			if( NULL != m_h264 )
			{
				fwrite(pNalData,1,nNalLen,m_h264);
			}
#endif
			
			nRet = DecodeVideoStream(m_pAvcDec,pNalData,nNalLen,&dec_Info);

			if( dec_Info.pic_type == AVCD_PIC_SPS )
			{
				if( m_nVideoWidth != 0 && m_nVideoHeight != 0 )
				{
                    if( m_nVideoWidth  != dec_Info.width && m_nVideoHeight != dec_Info.height )
					{
						if( NULL != m_pAvcDec )
						{
							CloseVideoDecoder(m_pAvcDec);
							m_pAvcDec = NULL;
						}

						m_nVideoWidth  = 0; 
						m_nVideoHeight = 0;

						goto ReStartDecode;
					}
				}

				m_nVideoWidth  = dec_Info.width;
				m_nVideoHeight = dec_Info.height;
			}

			if( nRet )
			{
				if( NULL == dec_Info.y )
				{
					return TG_ERR_FAILURE;
				}

				pOutDecInfo->pDecBuffer = (BYTE*)dec_Info.y;
				pOutDecInfo->lDecSize   = ( m_nVideoWidth * m_nVideoHeight * 3 ) >> 1;
				pOutDecInfo->nVideoWidth  = m_nVideoWidth;
				pOutDecInfo->nVideoHeight = m_nVideoHeight;
				pOutDecInfo->y = (BYTE*)dec_Info.y;
				pOutDecInfo->u = (BYTE*)dec_Info.u;
				pOutDecInfo->v = (BYTE*)dec_Info.v;
				pOutDecInfo->lDecPitch = dec_Info.linesize;

				return TG_ERR_SUCCESS;
			}
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER )
	{
	}

	return TG_ERR_FAILURE;
}

#elif H264_DEC_FFMPEG

static int g_RegAll = 1;

int TGVideoDecMan::Decode(BYTE* pEncFrame,long lEncSize,TG_VIDEO_DEC_INFO *pOutDecInfo)
{
	if( NULL == pEncFrame || 0 >= lEncSize || NULL == pOutDecInfo)
	{
		return TG_ERR_PARAMETER_ERROR;
	}

	pOutDecInfo->lColorSpace  = TG_COLSP_YV12;
	pOutDecInfo->nVideoWidth  = 0;
	pOutDecInfo->nVideoHeight = 0;
	pOutDecInfo->lDecSize     = 0;
	pOutDecInfo->pDecBuffer   = NULL;


ReStartDecode:

	if( NULL == c || NULL == picture )
	{
		if( g_RegAll )
		{
			/* register all the codecs */
			avcodec_register_all();
			g_RegAll = 0;
		}

		int nRet = 0;
		
		do 
		{
			av_init_packet(&avpkt);
			codec = avcodec_find_decoder(AV_CODEC_ID_H264);
			if (!codec) 
			{
				nRet = TG_ERR_FAILURE;
			}

			c = avcodec_alloc_context3(codec);
			picture= avcodec_alloc_frame();

			if(codec->capabilities&CODEC_CAP_TRUNCATED)
			{
				c->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
			}

			/* open it */
			if (avcodec_open2(c, codec, NULL) < 0) 
			{
				nRet = TG_ERR_FAILURE;
			}

		} while (FALSE);

		if( nRet < 0 )
		{
			UnInitialize();

			return nRet;
		}
	}


	TG_FRAME_HEAD *pFrameHead = (TG_FRAME_HEAD*)pEncFrame;
	long lNalOffset = pFrameHead->cHeadSize;

	TG_NAL_HEAD *pNalHead = NULL;
	UINT8       *pNalData = NULL;
	UINT32       nNalLen  = 0;

#ifdef dkq_write_264
	if( NULL != m_h264log )
	{
		fprintf( m_h264log, "num:%d,size:%d\n", pFrameHead->nFrameNum,pFrameHead->nFrameSize );
	}
#endif

	int nRet        = 0;
	int got_picture = 0;
	int	length      = 0 ;

	__try
	{
		while( lNalOffset < lEncSize )
		{
			if( 0x01 == (pFrameHead->cFrameType & 0x03) )
			{
				pNalHead = (TG_NAL_HEAD*)( pEncFrame + lNalOffset );
				lNalOffset += sizeof(TG_NAL_HEAD);
				pNalData = pEncFrame + lNalOffset;
				lNalOffset += pNalHead->lNalSize;
				if( pNalHead->cNalType < TG_NAL_SLICE_SPS || pNalHead->cNalType > TG_NAL_SLICE_IDR )
				{
					continue;
				}
				if( pNalHead->lNalSize <= 0 )
				{
					break;
				}
				nNalLen = pNalHead->lNalSize;
			}
			else
			{
				pNalData = pEncFrame + sizeof(TG_FRAME_HEAD);
				nNalLen  = pFrameHead->nFrameSize;

				lNalOffset += pFrameHead->nFrameSize;
			}

            avpkt.data = pNalData;
			avpkt.size = nNalLen;

			length = avcodec_decode_video2(c, picture, &got_picture, &avpkt);

			if( got_picture ) 
			{ 
				pOutDecInfo->pDecBuffer = (BYTE*)picture->data[0];
				pOutDecInfo->lDecSize   = ( c->width * c->height * 3 ) >> 1;
				pOutDecInfo->nVideoWidth  = c->width;
				pOutDecInfo->nVideoHeight = c->height;
				pOutDecInfo->y = (BYTE*)picture->data[0];
				pOutDecInfo->u = (BYTE*)picture->data[1];
				pOutDecInfo->v = (BYTE*)picture->data[2];
				pOutDecInfo->lDecPitch = picture->linesize[0];

				return TG_ERR_SUCCESS;
			}
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER )
	{
	}

	return TG_ERR_FAILURE;
}

#endif

